#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memdebug.h"

FILE    *__memdebug_fp;

guint emdg_mem_hash(gconstpointer k)
{
    return (guint)k;
}
gboolean emdg_mem_equal(gconstpointer l, gconstpointer r)
{
    return l == r;
}

GHashTable* 
emdg_mem_init(const char *filename)
{
    if (NULL != filename) {
        __memdebug_fp = fopen(filename, "a");
        if (NULL == __memdebug_fp) {
            __memdebug_fp = stdout;
        }
    }else{
        __memdebug_fp = stdout;
    }
    return g_hash_table_new(emdg_mem_hash, emdg_mem_equal);
}

void emdg_mem_destroy(GHashTable *ht)
{
    if (NULL != __memdebug_fp && __memdebug_fp != stdout) {
        fclose(__memdebug_fp);
    }
    g_hash_table_destroy(ht);
}

void*   
emdg_malloc (GHashTable *ht, int size, const char* filename, int line)
{
    MeM *m;
    gpointer    pot;

    if (NULL == ht)
        return NULL;
    
    m = mem_new(size, filename, line);
    if (NULL == m)
        return NULL;
   
    pot = m->pointer;
    //printf("k=%p v=%p\n", pot, m);
    g_hash_table_insert(ht, pot, m);
    //printf("k=%p v=%p %d\n", pot, g_hash_table_lookup(ht, pot), g_hash_table_size(ht));
    
    return m->pointer;
}

void    
emdg_free (GHashTable *ht, void* m, const char* filename, int line)
{
    MeM *mm;

    mm = g_hash_table_lookup(ht, m);
    //printf("===== free key=%p value=%p \n", m, mm);
    if (NULL == mm){
        fprintf(__memdebug_fp, "****** free error! not exist. %p file=%s line=%d\n", m, filename, line);
        return;
    }
    mem_delete(mm);
    g_hash_table_remove(ht, m);
}

static gboolean
_free_all(gpointer key, gpointer value, gpointer userdata)
{
    mem_delete((MeM*)value);
    return TRUE;
}


void    
emdg_free_all (GHashTable *ht)
{
    g_hash_table_foreach_remove(ht, _free_all, NULL);
}

static void
_print_all(gpointer key, gpointer value, gpointer userdata)
{
    MeM *m = (MeM*)value;
    fprintf(__memdebug_fp, "k=%p v=%p pointer=%p len=%d file=%s line=%d\n", key, value, m->pointer, m->len, m->file, m->line);
}


void
emdg_mem_print(GHashTable *ht)
{
    fprintf(__memdebug_fp, "size: %d\n", g_hash_table_size(ht));
    g_hash_table_foreach(ht, _print_all, NULL);

}


MeM*    
mem_new (int size, const char* filename, int line)
{
    MeM             *mm;
    void            *m;
    unsigned int    *flag;

    if (size <= 0) {
        return NULL;
    }
    
    m = (void*)malloc(sizeof(MeM) + size + sizeof(int));
    if (NULL == m)
        return NULL;
   
    mm = (MeM*)m;
    
    mm->len = size;
    strncpy(mm->file, filename, 31);
    mm->line = line;
    
    mm->pointer = m + sizeof(MeM);
    flag = mm->pointer + mm->len;
    *flag = 0x55555555;
    
    //printf("alloc pointer=%p len=%d file=%s line=%d\n", mm->pointer, mm->len, mm->file, mm->line);
    return mm;
}

void    
mem_delete (MeM* m)
{
    unsigned int *flag;

    if (NULL == m)
        return;

    if (m->pointer != m + 1) {
        fprintf(__memdebug_fp, "****** fatal error on free memory: pointer out of range! %x v=%p pointer=%p len=%d file=%s line=%d\n", *flag, m, m->pointer, m->len, m->file, m->line);
        return;
    }
    flag = m->pointer + m->len;
    //printf("===== flag: %p, m: %p, pointer: %p, len: %d\n", flag, m, m->pointer, m->len);
    //printf("===== delete   %p %d %x\n", m->pointer, m->len, *flag);

    if (*flag != 0x55555555) {
        fprintf(__memdebug_fp, "****** memory overstep! %x v=%p pointer=%p len=%d file=%s line=%d\n", *flag, m, m->pointer, m->len, m->file, m->line);
    }
    free(m);
}


